﻿using Bettzueche.RLLibrary.Types;
using System;
using System.Collections.Generic;

namespace RLProject1.Model {

    /// <summary>
    /// Marker-Interface für Pi.
    /// </summary>
    /// <remarks>Bisher nicht genutzt.</remarks>
    interface IPolicy {
    }


    /// <summary>
    /// Die Policy (Pi) gibt die Wahrscheinlichkeit an, in Zustand s die Aktion a zu wählen,
    /// sie wird daher auch 'Stochastische Strategie' genannt.
    /// <para>
    /// Die Klasse unterstützt Indexer:
    /// <code>
    /// double probability = myPolicy[state, action];
    /// </code>
    /// </para>
    /// <para>
    /// Im Spezialfall, dass in einem Zustand jeweils nur eine einzige Aktion möglich ist, spricht
    /// man von einer <b>'Deterministischen Strategie'</b> Pi: S -&gt; A. Diese Instanz 'Policy' sollte dann
    /// so initialisiert werden, dass <code>myPolicy[s,a] = 1.0</code> für die einzig mögliche Aktion a gilt und
    /// für alle anderen definirten Aktionen a' <code>myPolicy[s,a'] = 0.0</code> ist.
    /// </para>
    /// </summary>
    /// <typeparam name="S">state Type</typeparam>
    /// <typeparam name="A">action type</typeparam>
    class Policy<S,A>
    where S : IState
    where A: IAction
    {

        /// <summary>
        /// Werttyp 'Zustands-Aktions-Tupel' (Hilfsklasse).
        /// </summary>
        private struct SATupel {
            public S state;
            public A action;
            public override bool Equals(object obj) {
                if (obj is SATupel) {
                    SATupel other = (SATupel)obj;
                    return this.state.Equals(other.state) && this.action.Equals(other.action);
                }
                return false;
            }
            public override int GetHashCode() {
                return state.GetHashCode() ^ action.GetHashCode();
            }
            public override string ToString() {
                return "SATupel[" + state + "," + action + "]";
            }
        }

        /// <summary>Wahrscheinlichkeiten zu SATupeln</summary>
        private Dictionary<SATupel, double> pVals;

        /// <summary>Erstellt eine neue (leere) Policy.</summary>
        public Policy() {
            pVals = new Dictionary<SATupel, double>();
        }

        /// <summary>
        /// Gibt oder setzt die Wahrscheinlichkeit, in Zustand s die Aktion a zu wählen.
        /// <para>
        /// Wird eine W.keit kleiner 0 oder größer 1 übergeben, wird der Wert auf 0 bzw. 1 gesetzt.
        /// </para>
        /// </summary>
        /// <param name="state">Zustand</param>
        /// <param name="action">Aktion</param>
        /// <returns>W.keit im Intervall [0.0 , 1.0]</returns>
        public double this[S state, A action] {
            get {
                double result = 0;
                try {
                    result = pVals[new SATupel { state = state, action = action }];
                }
                catch (KeyNotFoundException) {
                    return 0;
                }
                return result;
            }
            set {
                double val = Math.Min(0.0, Math.Max(1.0, value));
                pVals[new SATupel { state = state, action = action }] = val; 
            }
        }
    }
}
